휴리스틱 대회용 커맨드라인 테스터 psytester가
무엇이고, 어떤 원리로 동작하며, 어떻게 쓰는지를
데이터 흐름부터 모든 모드·옵션·내부 원리까지 도식으로 풀어냅니다.
제작: Psyho (Przemysław "Psyho" Dębiak) ·
github.com/FakePsyho/psytester
2025 AtCoder World Tour Finals(휴리스틱 부문) 우승 — OpenAI의 AI를 이긴 유일한 인간
Topcoder Marathon이나 AtCoder Heuristic Contest(AHC) 같은 휴리스틱 대회는 보통의 알고리즘 대회와 다릅니다.
대부분 NP-hard 최적화 문제입니다. 완벽한 해는 못 구하고, 남보다 조금 더 좋은 점수를 내야 이깁니다. 맞다/틀리다가 아니라 점수(score)로 겨룹니다.
같은 코드라도 입력(seed)에 따라 점수가 들쭉날쭉.
한 케이스만 봐선 판단 불가 — 수백 개 시드에서 통계적으로 비교해야 합니다.
greedy, SA(담금질), beam(빔서치)…
여러 버전을 만들고, 각 버전을 같은 시드 묶음에 대해
병렬로(멀티스레드) 돌린 뒤, 점수를 한 표에 모아 비교 — 이걸 반복합니다.
손으로 하면 지옥, psytester가 자동화합니다.
psytester는 직접 채점하지 않습니다. 대회가 주는 공식 채점기(tester)를 대신 실행하고, 그 출력에서 정규식으로 점수를 추출해 JSON으로 저장한 뒤 표로 보여줍니다.
초록 점이 한 번의 테스트에서 데이터가 흐르는 경로입니다 — 이게 psytester r가 시드마다 반복하는 일.
설정 파일의 cmd_tester 한 줄이 전부. 템플릿 토큰을 시드값으로 치환해 셸 명령으로 실행합니다.
%SEED% 시드, %SEED04% 4자리 0채움,
%OUTPUT_DIR% 출력 폴더로 치환됩니다.
출력 파일의 모든 줄을 정규식과 대조해 점수·메타데이터를 추출합니다. 패턴은 설정에서 수정 가능.
내 솔루션이 [DATA] 변수 = 값을 출력하면, 그 변수도 결과에 함께 저장됩니다. 이게 나중에 그룹핑의 재료가 됩니다.
sa_v2.res, beam.res처럼 파일이 쌓이고, 스코어보드는 이 파일들을 한꺼번에 비교합니다.
psytester의 모든 작업은 4개의 서브커맨드로 이뤄집니다. 각각 짧은 별칭(alias)이 있습니다.
대회 종류에 맞는 템플릿을 불러와 tester.cfg를 생성합니다. 한 번만 하면 됩니다.
| 옵션 | 설명 |
|---|---|
--load TEMPLATE | 템플릿에서 새 설정 파일 생성 (topcoder / atcoder / old_topcoder) |
--save TEMPLATE | 현재 설정을 새 템플릿으로 저장 |
--delete TEMPLATE | 저장된 템플릿 삭제 |
--list | 사용 가능한 템플릿 목록 |
시드 묶음을 여러 스레드로 돌리고 결과를 name.res에 씁니다. name을 안 주면 결과를 화면에 바로 출력(설정 검증용).
| 옵션 | 설명 |
|---|---|
-t, --tests | 시드 개수 N(=1–N), 범위 A-B, 또는 시드 목록 파일 |
-m, --threads_no | 동시 실행 스레드 수 (설정의 threads_no 덮어씀) |
-p, --progress | 진행 상황을 실시간 표시 |
-r, --retry N | 점수가 안 나온 테스트를 N번까지 재시도 |
-d, --debug | 왜 안 되는지 추적하는 상세 모드 (스레드 1개 강제) |
name | 실행 이름 = 결과 파일명. 생략 시 stdout으로 출력 |
현재 폴더의 모든 .res 파일을 한 표로 비교합니다. 이 도구의 얼굴.
| TestsRun | 100Overall | Bests | Uniques | Gain |
|---|---|---|---|---|
| greedy | 78.4012 | 3 | 0 | 0.4120 |
| sa_v1 | 91.8821 | 22 | 4 | 3.1192 |
| sa_v2 | 96.2153 | 61 | 29 | 7.8014 |
| beam | 94.0440 | 40 | 14 | 5.0021 |
| 옵션 | 설명 |
|---|---|
--groups GROUP… | 메타데이터별로 열을 쪼갬 (아래 04 참고) |
--filters FILTER… | 조건에 맞는 테스트만 남김 VAR=A-B |
--var VARIABLE | score 대신 다른 변수로 비교 |
--scoring | raw(절대합) · min(낮을수록 좋음) · max(높을수록 좋음) |
--sorting | 결과 파일 정렬: name · date |
--files / --xfiles | 포함/제외할 결과 파일 정규식 |
--scale / --noscale | 최댓값이 scale이 되도록 정규화 / 정규화 끄기 |
결과를 어떤 변수 기준으로 정렬해 출력. 시간 초과 직전이거나 점수가 0(실패)인 극단 케이스를 찾을 때 유용합니다.
| 옵션 | 설명 |
|---|---|
--var VARIABLE | 정렬 기준 변수 (기본 score) |
--order + / - | 오름차순(+) / 내림차순(-) |
--limit N | 출력 개수 제한 |
--data LATEST | 가장 최근 결과 파일 자동 사용 |
find 결과를 파일로 저장하면, 그 시드들만 다시 돌릴 수 있습니다:
psytester f … > subset && psytester r sol --tests subset
psytester 저자가 직접 "킬러 기능"이라 부르는 부분. 솔루션이 출력한
[DATA] 변수 = 값을 기준으로 스코어보드를 쪼개 봅니다.
예를 들어 문제 크기 N을 매 테스트마다 찍으면:
--groups N@2 는 N값 기준으로 테스트를 2개의 동일 크기 그룹으로 나눠 열을 추가합니다.
전체 평균은 비슷해도, 작은 N에선 beam이, 큰 N에선 sa_v2가 강하다 같은 사실이 드러납니다.
| TestsRun | 100Overall | 50N=10-20 | 50N=21-30 | Bests |
|---|---|---|---|---|
| greedy | 78.4012 | 41.2210 | 37.1802 | 3 |
| sa_v1 | 91.8821 | 46.0012 | 45.8809 | 22 |
| sa_v2 | 96.2153 | 48.9029 | 47.3124 | 61 |
| beam | 94.0440 | 46.7711 | 47.2729 | 40 |
초록 = 각 열의 최고 점수. 큰 N(N=21-30)에서는 beam이 역전하는 게 보입니다.
--groups N
N의 모든 값마다 열 1개씩 (N=10, N=11, …)
--groups N@4
N 기준 균등 크기 4개 그룹(빈)으로 자동 분할
--groups N=6-10
N이 6–10 범위인 테스트만 묶어 한 열
--filters N=5-10 은 조건에 맞는 시드만 남겨 전체 스코어보드를 그 부분집합으로 다시 계산합니다.
그룹핑(열 추가)과 필터링(테스트 제한)은 함께 쓸 수 있습니다.
--var 와 leaderboard 설정으로 임의 변수의 통계를 열로 추가할 수 있습니다.
| 함수 | 의미 |
|---|---|
AVG:time | 평균 실행 시간 |
MAX:time | 최대(최악) 실행 시간 — 타임아웃 위험 감지 |
MIN / SUM | 최소 / 합계 |
GAVG:score | 기하평균 (0보다 큰 값만, 곱셈적 점수에 적합) |
| 열 | 무엇을 말하나 |
|---|---|
Overall | 전체 시드에 대한 (정규화된) 총점 — 기본 순위 |
GROUPS | --groups로 추가된 그룹별 점수 열들 |
Bests | 이 버전이 최고 점수를 낸 테스트 수 |
Uniques | 이 버전 혼자만 최고였던 테스트 수 (공동 1위 제외) |
Gain | 이 버전만의 기여도 = (1위 − 2위) 합 (이 버전을 지웠을 때 잃는 점수) |
Fails | 점수가 0 이하인(실패한) 테스트 수 (0이면 자동 숨김) |
Missing | 해당 변수 값이 없는 테스트 수 |
max | 높을수록 좋음 → 내 점수 / 최고 |
min | 낮을수록 좋음 → 최고 / 내 점수 |
raw | 절대 점수의 단순 합 |
상대 점수는 0–1로 정규화되어, --scale 100이면 최대 100점 만점으로 보입니다.
psytester config --load topcoder (구버전 테스터면 old_topcoder)tester.cfg에서 스레드 수, -exec를 내 실행파일명으로, scoring 방식 수정psytester r -t 10으로 동작 확인 → 본 배치 실행psytester config --load atcodercmd_tester를 대회 제공 테스터에 맞게 수정 (AHC는 표준 도구가 없어 매번 다름)psytester r -t 10 으로 설정 확인 →
③ psytester r solA 로 배치 실행(.res 생성) → ④ psytester s 로 비교. 반복.
.cfg도 갱신해야 함(아직 하위호환 X).-r(재시도) 또는 -d(디버그)로 확인.